New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Ordered Collection Diffing #21845
Conversation
Nit: stdlib uses 2 space tabs |
…k, causing some strange crashes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@numist several lines of this patch are too long. Stdlib uses 2 spaces tabs and has a maximum line width set to 80 characters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had suggested a number of small(ish) adjustments -- mostly to follow the stdlib coding guidelines:
- The fully qualified names of internal/private types (and members) must have at least one underscore-prefixed component. For initializers, we put the underscore in the label of the first parameter.
- All declarations must have an explicit access level in the stdlib. (This is often redundant, but it helps a lot with reviewing what gets into the public API/ABI.)
- When possible, we prefer to have meaningful names for generic type parameters, function parameters and local variables. (I think
x
,y
etc. works fine in the core diffing implementation, though.)
- Add an underscore to private/internal symbols - Make access levels explicit, even when they’re implied from context - Conform to max line length of 80 characters - Conformances declared on separate extensions that implement them - Arrange member declarations so that stored properties appear first - Expand single-letter function and type parameter names where there is an obvious name that’s more descriptive - RangeReplaceableCollection.fastApplicationEnumeration → CollectionDifference._fastEnumeratedApply
Suggested by @DevAndArtist Co-Authored-By: numist <github@numist.net>
Preparations for landing numist/swift
I hope it won't get shipped with |
public init?<Changes: Collection>( | ||
_ changes: Changes | ||
) where Changes.Element == Change { | ||
if !CollectionDifference<ChangeElement>._validateChanges(changes) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW: there's an idiomatic way to spell if !
called guard
. Some people prefer it for preconditions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, this actually used to be a guard
but the code got refactored a number of times >_<
} | ||
|
||
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) // FIXME(availability-5.1) | ||
extension CollectionDifference.Index { // Comparable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't the conformance be removed from the declaration above, and uncommented here?
|
||
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) // FIXME(availability-5.1) | ||
extension CollectionDifference { | ||
fileprivate func _fastEnumeratedApply( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that private and fileprivate can work (but not as usableFromInline) in a world where we're not sil-serialize-all
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We started to occasionally use private in the stdlib last year. It's still a bit unusual, and the fileprivate/private mess has annoying interactions with our rule for explicit access levels. But as far as I know, the technical issues we had before have all been resolved now.
stdlib/public/core/Diffing.swift
Outdated
func append( | ||
into target: inout Self, | ||
contentsOf source: Self, | ||
from index: inout Self.Index, count: Int) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The convention is to put a closing paren and the opening curly on the new line unindented after the parameter list.
stdlib/public/core/Diffing.swift
Outdated
enumeratedOriginals += origCount | ||
enumeratedInserts += 1 | ||
} | ||
assert(enumeratedOriginals <= self.count) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert
is rarely the right choice for the stdlib code. Consider using _precondition
, _debugPrecondition
, or _internalInvariant
as described here.
stdlib/public/core/Diffing.swift
Outdated
} | ||
|
||
fileprivate subscript(position: Index) -> Element { | ||
precondition((startIndex..<endIndex).contains(position)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/precondition/_precondition
perhaps?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think _internalInvariant
is actually more appropriate here since this only provides early detection of a code defect that would fail further along regardless. I'll go through all the calls to assert
and update as appropriate.
var (x, y) = (x, y) | ||
let (n, m) = (a.endIndex, b.endIndex) | ||
|
||
var v = _SearchState<Source.Index, Target.Index>(consuming: &pathStorage) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Vrbl nms R nt vry rdbl.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, too used to writing pure C—vowels are expensive!
For what it's worth, in this specific case the variable names are cribbed directly from the pseudocode in the Myers' paper (linked in the comments of this file) so they are canonical.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the terse names are justified here since the implementation follows the paper, and using the same notation makes the code easier to follow.
test/stdlib/Diffing.swift
Outdated
|
||
for (source, target, expected, line) in expectedChanges { | ||
let actual = target.difference(from: source).inferringMoves() | ||
expectEqual(actual, CollectionDifference(expected), "failed test at line \(line)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StdlibUnittest expect
functions take expected first and actual second.
test/stdlib/Diffing.swift
Outdated
let mine = "Is\nit\nreview\ntime\nalready?" | ||
|
||
// Split the contents of the sources into lines | ||
let baseLines = base.components(separatedBy: "\n") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it an NSString API? How is it even available here without import Foundation
, I wonder.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe through StdlibUnittest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've only run the tests on Darwin, it seems likely that Foundation is getting imported somewhere. I'll make the test more literal.
test/stdlib/Diffing.swift
Outdated
let expectedChanges: [( | ||
source: [String], | ||
target: [String], | ||
changes: [CollectionDifference<String>.Change], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh.. So we don't need availability checks if the APIs are marked as 9999?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, good catch -- I haven't run the tests after the availability commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still have to guard for it, but it will succeed at runtime
[test] Add missing runtime availability check around diffing tests
@swift-ci please test |
Build failed |
@swift-ci please test |
Build failed |
Build failed |
@swift-ci please test |
Build failed |
Build failed |
The macOS failure in Swift Syntax was fixed in apple/swift-syntax#106.
|
Third time the charm! @swift-ci test |
@lorentey how did u do that |
This PR is an initial implementation of the Ordered Collection Diffing proposal, which was pitched on the forums in December.